package com.knowvoyage.knowvoyageserver.config;

import com.knowvoyage.knowvoyageserver.util.JwtUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter;
import org.springframework.security.web.server.context.NoOpServerSecurityContextRepository;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsConfigurationSource;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import reactor.core.publisher.Mono;

import java.util.Collections;

@Configuration
@EnableWebFluxSecurity
@Slf4j
public class SecurityConfig {

    private final JwtUtil jwtUtil;

    public SecurityConfig(JwtUtil jwtUtil) {
        this.jwtUtil = jwtUtil;
    }

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        return http
                .cors(cors -> cors.configurationSource(corsConfigurationSource()))
                .csrf(ServerHttpSecurity.CsrfSpec::disable)
                .formLogin(ServerHttpSecurity.FormLoginSpec::disable)
                .httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)
                .securityContextRepository(NoOpServerSecurityContextRepository.getInstance())
                .authorizeExchange(exchanges -> exchanges
                                .pathMatchers(
                                "/**",
                                        "/auth/**",
                                        "/v3/**",
                                        "/webjars/**"
                                ).permitAll()
                                .anyExchange().authenticated()
                )
                .addFilterAt(jwtAuthenticationFilter(), SecurityWebFiltersOrder.AUTHENTICATION)
                .exceptionHandling(handling -> handling
                        .authenticationEntryPoint((exchange, ex) -> {
                            log.info("jwt认证未通过， URI ：{}", exchange.getRequest().getURI());
                            exchange.getResponse().setRawStatusCode(401);
                            return exchange.getResponse().writeWith(Mono.just(
                                    exchange.getResponse().bufferFactory()
                                            .wrap("{\"code\":401,\"message\":\"Unauthorized\"}".getBytes())
                            ));
                        })
                        .accessDeniedHandler((exchange, ex) -> {
                            log.info("jwt认证通过但权限不足， URI ：{}", exchange.getRequest().getURI());
                            exchange.getResponse().setRawStatusCode(403);
                            return exchange.getResponse().writeWith(Mono.just(
                                    exchange.getResponse().bufferFactory()
                                            .wrap("{\"code\":403,\"message\":\"You can`t into\"}".getBytes())
                            ));
                        })
                )
                .build();
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowedOriginPatterns(Collections.singletonList("*"));
        config.setAllowedMethods(Collections.singletonList("*"));
        config.setAllowedHeaders(Collections.singletonList("*"));
        config.setAllowCredentials(true);
        config.setMaxAge(3600L);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return source;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    private AuthenticationWebFilter jwtAuthenticationFilter() {
        AuthenticationWebFilter filter = new AuthenticationWebFilter(authenticationManager());
        filter.setServerAuthenticationConverter(jwtAuthenticationConverter());
        return filter;
    }

    private ServerAuthenticationConverter jwtAuthenticationConverter() {
        return exchange -> {
            String authHeader = exchange.getRequest()
                    .getHeaders()
                    .getFirst("Authorization");

            if (authHeader == null || !authHeader.startsWith("Bearer ")) {
                return Mono.empty();
            }

            String token = authHeader.substring(7);
            return Mono.just(token)
                    .flatMap(t -> {
                        try {
                            Claims claims = jwtUtil.parseToken(t);
                            Long userId = claims.get("id", Long.class);
                            String phone = claims.get("phone", String.class);

                            // 将用户信息存入请求属性（可选）
                            exchange.getAttributes().put("userId", userId);
                            exchange.getAttributes().put("phone", phone);

                            return Mono.just(new UsernamePasswordAuthenticationToken(
                                    userId,
                                    null,
                                    Collections.emptyList()
                            ));
                        } catch (ExpiredJwtException e) {
                            log.info("Token 已过期");
                            return Mono.empty();
                        } catch (JwtException e) {
                            log.info("Token 解析错误");
                            return Mono.empty();
                        } catch (IllegalArgumentException e) {
                            log.info("Token 为空");
                            return Mono.empty();
                        }
                    });
        };
    }

    @Bean
    public ReactiveAuthenticationManager authenticationManager() {
        return authentication -> {
            if (authentication instanceof UsernamePasswordAuthenticationToken) {
                return Mono.just(authentication);  // 认证通过
            }
            return Mono.error(new BadCredentialsException("Unsupported authentication"));
        };
    }
}
